#include "gamelogic.h"
#include "utils.h"
#include "JlCompress.h"
#include "Windows.h"

#if defined(GM_PLAT_ZTJ_TOP1GAME)
#include "ztj_top1game_cliwindowsimple.h"
#else
#include "cliwindow.h"
#endif

#include <QFileInfo>
#include <QUrl>
#include <QDateTime>
#include <QMenu>
#include <QApplication>
#include <QOperatingSystemVersion>
#include <QPushButton>

struct EnumWindowParam
{
    int processId;
    HWND hander;
};

GameLogic::GameLogic()
    : QObject()
{
    cfgMgr.readCommonConfig(PathUtils::resource("fyconfig.ini"));

    connect(&m_dlMgr, &DownloadManager::start, this, &GameLogic::onDownloadStart);
    connect(&m_dlMgr, &DownloadManager::progress, this, &GameLogic::onDownloadProgress);
    connect(&m_dlMgr, &DownloadManager::milestone, this, &GameLogic::onDownloadMilestone);
    connect(&m_dlMgr, &DownloadManager::finished, this, &GameLogic::onDownloadFinished);
    connect(&m_dlMgr, &DownloadManager::error, this, &GameLogic::onDownloadError);
}

GameLogic::~GameLogic()
{
    delete m_window;
}

void GameLogic::setUp(QApplication *app, QList<QString> argList, std::unique_ptr<std::map<QString, QString>> argMap)
{
    m_app = app;
    m_argList = argList;
    m_argMap = std::move(argMap);

    // 根据不同平台创建不同的面板
#if defined (GM_PLAT_ZTJ_TOP1GAME)
    // 诛天记台服启用simple模式，没有选服页
    #ifndef GM_MODE_SIMPLE
        #define GM_MODE_SIMPLE
    #endif
    // 诛天记台服启动参数只有token，写死固定平台
    #ifndef GM_FIXED_PLATFORM
        #define GM_FIXED_PLATFORM "tw"
    #endif
    // 使用指定资源集
    #ifndef GM_RES_PREFIX
        #define GM_RES_PREFIX ":/ztj_top1game/res/ztj_top1game/"
    #endif

    m_window = new Ztj_Top1game_CliWindowSimple();
    m_vars.token = argList[0];
#else
    // 默认模式，带选服页
    // 使用默认资源集
    #ifndef GM_RES_PREFIX
        #define GM_RES_PREFIX ":/myRes/res/"
    #endif

    m_window = new CliWindow();
#endif

    QCoreApplication::setApplicationName(m_window->windowTitle());
    connect(m_window, &ICliWindow::onEnterGame, this, &GameLogic::onEnterGame);

    // 检查参数 - 微端模式
#if defined(GM_MODE_SIMPLE)
    // 强制开启simple模式
    m_vars.mode = GameMode::Simple;
#else
    if(m_argMap->find(PARAM_SIMPLEMODE) != m_argMap->end())
    {
        m_vars.mode = GameMode::Simple;
    }
    else if(m_argMap->find(PARAM_HIDE) != m_argMap->end())
    {
        m_vars.mode = GameMode::Hide;
    }
    else
    {
        m_vars.mode = GameMode::Normal;
    }
#endif

    // 检查参数 - 平台类型
#if defined(GM_FIXED_PLATFORM)
    m_vars.platform = GM_FIXED_PLATFORM;
    qInfo() << "使用固定平台参数";
#endif
    if(m_argMap->find(PARAM_PLATFORM) != m_argMap->end())
    {
        m_vars.platform = m_argMap->at(PARAM_PLATFORM);
    }
    else
    {
        auto pfInCfg = cfgMgr.readString<QString>(CONFIG_KEY_PLATFORM);
        if(!pfInCfg.isEmpty())
        {
            m_vars.platform = pfInCfg;
            qInfo() << "使用fyconfig.ini中的平台参数";
        }
    }

    if(m_vars.platform.isEmpty())
    {
        qErrnoWarning("未指定平台参数！");
        m_app->exit(1);
        return;
    }
    qInfo() << "game mode:" << m_vars.mode << ", platform:" << m_vars.platform;

    // 检查参数 - unity命令key
    if(m_argMap->find(PARAM_CMD) != m_argMap->end())
    {
        // 微端安装器首次安装微端后会同时拉起unity，不需要微端操作，这种情况下安装器会将cmd参数传给微端和unity
        m_unityCmdKey = m_argMap->at(PARAM_CMD);
    }

    // 系统托盘图标
    m_sysTray.setToolTip(QCoreApplication::applicationName() + QCoreApplication::applicationVersion());
    m_sysTray.setIcon(QIcon(QString(GM_RES_PREFIX) + "icon_64.ico"));
    QMenu *menu = new QMenu();
    menu->addAction(ICON_MENU_EXIT, this, &GameLogic::onExitMenu);
    m_sysTray.setContextMenu(menu);
    connect(&m_sysTray, &QSystemTrayIcon::activated, this, &GameLogic::onSystemTrayIconActivated);
    m_sysTray.show();

    if(GameMode::Hide == m_vars.mode)
    {
        // 安装器安装后会直接启动unity，同时以后台模式启动微端
        // 后台模式直接开启定时器监听unity命令
        startTimer(TimerTask::CheckUnityCmd);
    }
    else
    {
        // 后台模式既不显示微端面板，也不启用Qos
        m_window->show();

        auto osver = QOperatingSystemVersion::current();
        m_qos.setQosEnv(cfgMgr.readString<QString>(CONFIG_KEY_QOS), QCoreApplication::applicationVersion(), osver.name());
    }

    // 平台配置
    auto platCfgName = cfgMgr.readString<QString>(CONFIG_KEY_PLATFORMCFGNAME);
    Q_ASSERT(!platCfgName.isEmpty());

    platCfgName = StringUtils::formatQString(platCfgName, m_vars.platform);
    m_vars.pfIni = PathUtils::resource(platCfgName);

    // 开始检查平台配置
    m_state = GameState::DownloadPlatCfg;
    if(QFile::exists(m_vars.pfIni))
    {
        // 平台配置已存在
        qInfo() << "平台配置已存在：" << m_vars.pfIni;
        onDownloadFinished();
    }
    else
    {
        // 开始下载平台配置
        auto pfRawURL = cfgMgr.readString<QString>(CONFIG_KEY_PLATFORMURL);
        auto pfURL = StringUtils::formatQString(pfRawURL, platCfgName);
        qInfo() << "开始下载平台配置:" << pfURL;
        appendDownload(DownloadTask::DT_PlatCfg, QUrl(pfURL), DOWNLOAD_PLATCFG, Qos::QosType::DOWNLOAD_PFCFG_ERR, "downloadpferr");
    }
}

void GameLogic::onExitMenu()
{
    qInfo() << "点击托盘图标退出菜单";
    safeExit();
//    if(1 != cfgMgr.readString<int>(CONFIG_KEY_DISABLEXIT) || m_canExit)
//    {
//        safeExit();
//    }
//    else
//    {
//        qInfo() << "不可退出:" << cfgMgr.readString<int>(CONFIG_KEY_DISABLEXIT) << "," << m_canExit;
//    }
}

BOOL IsMainWindow(HWND handle)
{
    return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}

BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam)
{
    EnumWindowParam &data = *(EnumWindowParam*)lParam;
    unsigned long processId = 0;
    GetWindowThreadProcessId(handle, &processId);
    if(data.processId != processId || !IsMainWindow(handle))
    {
        return TRUE;
    }
    data.hander = handle;
    return FALSE;
}

void GameLogic::onSystemTrayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
    if(QSystemTrayIcon::ActivationReason::DoubleClick == reason)
    {
        // 查找游戏窗口并最大化
        if(nullptr != m_unityProc)
        {
            struct EnumWindowParam data;
            data.processId = m_unityProc->processId();
            data.hander = 0;
            EnumWindows(EnumWindowsCallback, (LPARAM)&data);
            if(data.hander)
            {
                qInfo() << "游戏窗口已打开，直接激活到最前面";
                ShowWindow(data.hander, SW_SHOWMAXIMIZED);
                SetForegroundWindow(data.hander);
                return;
            }
        }
        qInfo() << "未找到游戏窗口";
        resetWindow();
    }
}

void GameLogic::resetWindow()
{
    if(!m_window->isVisible())
    {
        m_window->setVisible(true);
    }
    if(m_window->isMinimized())
    {
        m_window->showNormal();
    }
    qInfo() << "置顶窗口:" << SetForegroundWindow((HWND)m_window->winId());
    if(GameMode::Simple != m_vars.mode && !m_vars.enterQuery.isEmpty())
    {
        m_window->gotoURL(cfgMgr.readString<QString>(CONFIG_KEY_PAGE), cfgMgr.readString<QString>(CONFIG_KEY_JUMP));
    }
}

void GameLogic::checkPkg()
{
    if(m_state >= GameState::QueryMin && m_state <= GameState::UnzipPkg)
    {
        qInfo() << "已经在检查游戏包了:" << m_state;
    }
    qInfo() << "开始检查游戏包";
    Q_ASSERT(m_vars.minVer == 0);

    m_qos.report(Qos::QosType::QUERY_MIN_START, "startquerymin");
    m_state = GameState::QueryMin;
    auto rawShellCfgURL = cfgMgr.readString<QString>(CONFIG_KEY_SHELLCFGURL);
    auto shellCfgURL = StringUtils::formatQString<int>(rawShellCfgURL, static_cast<int>(QDateTime::currentSecsSinceEpoch()));
    auto url = QUrl(shellCfgURL);
    m_vars.shellCfg = PathUtils::resource(url.fileName());
    // 删除旧配置
    QFile::remove(PathUtils::resource(QFileInfo(url.path()).fileName()));
    appendDownload(DownloadTask::DT_ShellCfg, url, DOWNLOAD_SHELLCFG, Qos::QosType::DOWNLOAD_SHELLCFG_ERR, "queryminerr");
}

void GameLogic::unzipGameZip()
{
    m_qos.report(Qos::QosType::UNZIP_PKG_START, "startunzip");

    qInfo() << "开始解压zip:" << m_vars.localPkg;

    auto list = JlCompress::extractDir(m_vars.localPkg, PathUtils::resource(""));
    QFile::remove(m_vars.localPkg);

    qInfo() << "解压结束，文件数量:" << list.size();
    if(list.size() == 0)
    {
        m_qos.report(Qos::QosType::UNZIP_PKG_ERR, "unziperr");
        QMessageBox msgbox(QMessageBox::Icon::Critical, UIUtils::winTitle(WIN_TITLE_ERROR), MSG_UNZIP_ERROR);
        msgbox.exec();
    }
    else
    {
        m_qos.report(Qos::QosType::UNZIP_PKG_FINISH, "finishunzip");
        onPkgReady();
    }
}

void GameLogic::onPkgReady()
{
    m_state = GameState::WaitEnterGame;
    if(m_vars.enterQuery.isEmpty())
    {
#if defined (GM_PLAT_ZTJ_TOP1GAME)
        // 诛天记独立端拉取游戏链接
        auto loginUrl = cfgMgr.readString<QString>(CONFIG_KEY_BOXLOGIN).arg(m_vars.token);
        qInfo() << "登录游戏盒子:" << loginUrl;
        QNetworkRequest req;
        req.setUrl(QUrl(loginUrl));
        connect(&m_netMgr, &QNetworkAccessManager::finished, this, &GameLogic::onZtjTop1GameLogin);
        m_netMgr.get(req);
#else
        // 检查更新
        checkUpdate();
#endif
    }
    else
    {
        launchUnity();
    }
}

bool GameLogic::checkD3d9()
{
    QLibrary lib("d3d9");
    lib.load();
    if(lib.isLoaded())
    {
        lib.unload();
        return true;
    }
    else
    {
        return false;
    }
}

void GameLogic::onEnterGame(const QString &query)
{
    qInfo() << "onEnterGame, query:" << query << ", state:" << m_state;
    m_vars.enterQuery = query;
    m_qos.report(Qos::QosType::QUERY_OPEN_GAME, "opengame");
    if(GameState::WaitEnterGame == m_state)
    {
        launchUnity();
    }
}

void GameLogic::launchUnity()
{
    // 检查DX9
    if(checkD3d9())
    {
        // 已安装DX9，启动unity
        qInfo() << "已安装dx9，启动unity";

        auto pkgName = cfgMgr.readString<QString>(CONFIG_KEY_PKGNAME);
        QString program = PathUtils::resource(pkgName + "/" + pkgName + ".exe");
        QStringList args;
        // 为兼容斗罗，参数以xxx:yyy形式
        auto queryPairs = m_vars.enterQuery.split("&");
        for(auto &queryPair : queryPairs)
        {
            auto queryPairArr = queryPair.split("=");
            auto paramKey = queryPairArr[0];
            QString paramValue;
            if(paramKey == "qid" || paramKey == "uid")
            {
                m_vars.uid = paramKey;
                m_qos.setUID(paramKey);
            }
            if(queryPairArr.size() == 2)
            {
                paramValue = queryPairArr[1];
            }
            else
            {
                paramValue = QString();
            }
            args << paramKey + ":" + paramValue;
        }
        // Qos时间戳
        args << "qosStartAt:" + QString::number(m_qos.getStartTime());
        // 通信key
        m_unityCmdKey = QString::number(QDateTime::currentSecsSinceEpoch());
        args << "cmdKey:" + m_unityCmdKey;
        // 微端版本号
        args << "wdver:3";
        // 微端路径
        args << "shell:" + QCoreApplication::applicationFilePath();
        // log开关
        auto logParams = cfgMgr.readString<QString>(CONFIG_KEY_LOG);
        if(!logParams.isEmpty())
            args << logParams;
        // 附加参数
        auto extraParams = cfgMgr.readString<QString>(CONFIG_KEY_PARAMS);
        if(!extraParams.isEmpty())
            args << extraParams;
        qInfo() << "启动参数:" << args;

        m_unityProc = std::make_unique<QProcess>(new QProcess(this->parent()));
        connect(m_unityProc.get(), &QProcess::started, this, &GameLogic::onUnityStarted);
        connect(m_unityProc.get(), &QProcess::errorOccurred, this, [=](QProcess::ProcessError error)->void{
            qInfo() << "unity进程创建失败，错误码:" << error;
            m_qos.report(Qos::QosType::LAUNCH_UNITY_ERR, "unityerr");
            QMessageBox msgbox(QMessageBox::Icon::Critical, UIUtils::winTitle(WIN_TITLE_ERROR), MSG_CALL_UNITY_TIP);
            msgbox.exec();
        });
        m_unityProc->start(program, args);

        m_window->setHidden(true);
    }
    else
    {
        // 未安装DX9，提示安装
        qInfo() << "未安装dx9，提示安装";
        m_qos.report(Qos::QosType::LAUNCH_UNITY_NODX, "nodx9");
        QMessageBox msgbox(QMessageBox::Icon::Critical, UIUtils::winTitle(WIN_TITLE_ERROR), MSG_DX9_ERROR);
        msgbox.exec();
    }
}

bool GameLogic::isUnityRunning()
{
    return m_unityProc && QProcess::ProcessState::NotRunning != m_unityProc->state();
}

QString GameLogic::findUnityLog()
{
    auto pkgName = cfgMgr.readString<QString>(CONFIG_KEY_PKGNAME);
    QString logFile = PathUtils::resource(pkgName + "/" + pkgName + "_Data/output_log.txt");
    if(!QFile::exists(logFile))
    {
        auto productName = cfgMgr.readString<QString>(CONFIG_KEY_PRODUCTNAME);
        if(!productName.isEmpty())
        {
            // 查找AppData
            logFile = QDir::homePath() + "AppData/LocalLow/" + productName + "/Player.log";
            if(!QFile::exists(logFile))
            {
                return "";
            }
        }
    }
    return logFile;
}

void GameLogic::onUnityStarted()
{
    qInfo() << "unity进程创建成功，进程ID:" << m_unityProc->processId();
    if(m_callUnityAt == 0)
        m_callUnityAt = QDateTime::currentSecsSinceEpoch();
    startTimer(TimerTask::CheckUnityLog | TimerTask::CheckUnityCmd);
}

void GameLogic::startTimer(int taskFlag)
{
    m_timerTaskFlag = taskFlag;
    if(nullptr == m_timer)
    {
        m_timer = std::make_unique<QTimer>(new QTimer(this));
        connect(m_timer.get(), &QTimer::timeout, this, &GameLogic::onTimer);
        m_timer->start(500);
    }

}

void GameLogic::appendDownload(DownloadTask task, const QUrl &url, const QString &desc,
                               Qos::QosType errQosType, const QString &errQosDesc)
{
    struct DownloadItem item;
    item.task = task;
    item.desc = desc;
    item.errQosType = errQosType;
    item.errQosDesc = errQosDesc;
    m_downloadTaskMap[url] = item;
    m_dlMgr.append(url);
}

void GameLogic::onDownloadStart(const QUrl &url)
{
    auto item = m_downloadTaskMap[url];
    if(m_window)
    {
        m_window->setProgressTip(item.desc);
    }
}

void GameLogic::onDownloadProgress(const QUrl &url, qint64 bytesReceived, qint64 bytesTotal)
{
    m_window->setProgressValue(bytesReceived, bytesTotal);
}

void GameLogic::onDownloadMilestone(const QUrl &url)
{
    auto item = m_downloadTaskMap[url];
    if(DownloadTask::DT_Shell == item.task)
    {
        continueUpdate();
    }
}

void GameLogic::onDownloadFinished()
{
    m_window->setProgressTip(nullptr);
    if(GameState::DownloadPlatCfg == m_state)
    {
        qInfo() << "平台配置就绪";
        cfgMgr.readPlatConfig(m_vars.pfIni);

        if(GameMode::Simple != m_vars.mode)
        {
            m_window->gotoURL(cfgMgr.readString<QString>(CONFIG_KEY_PAGE), cfgMgr.readString<QString>(CONFIG_KEY_JUMP));
        }

        m_qos.report(Qos::QosType::PAGE_READY, "_PageReady");
        checkPkg();
    }
    else if(GameState::QueryMin == m_state)
    {
        qInfo() << "ShellCfg下载完成";
        cfgMgr.readShellConfig(m_vars.shellCfg);

        m_vars.minVer = cfgMgr.readString<QString>(CONFIG_KEY_MINVER).toInt();
        qInfo() << "在线获取最低版本号:" << m_vars.minVer;

        auto crtVer = FileUtils::readTxt("pkg.ver", "0").toInt();
        qInfo() << "本地版本为" << m_vars.minVer << "最低版本为" << crtVer;

        auto pkgName = cfgMgr.readString<QString>(CONFIG_KEY_PKGNAME);
        if(crtVer != m_vars.minVer)
        {
            // 版本不对，重新下载新包
            qInfo() << "本地pkg已过期，删除重下";
            QDir localPkg = QDir(PathUtils::resource(pkgName));
            localPkg.removeRecursively();
        }

        auto gameExe = QFile(PathUtils::resource(pkgName + "/" + pkgName + ".exe"));
        if(!gameExe.exists())
        {
            auto pkgURL = cfgMgr.readString<QString>(CONFIG_KEY_PKG);
            pkgURL.replace("{0}", QString::asprintf("%05d", m_vars.minVer));
            pkgURL += QString::asprintf("?ver=%lld", QDateTime::currentSecsSinceEpoch());
            auto url = QUrl(pkgURL);
            m_vars.localPkg = PathUtils::resource(url.fileName());

            if(QFile::exists(m_vars.localPkg))
            {
                // 有游戏包zip，直接解压
                qInfo() << "zip已下载，直接解压游戏包:" << m_vars.localPkg;
                unzipGameZip();
            }
            else
            {
                qInfo() << "pkg包不存在:" << m_vars.localPkg;
                // 写入版本号
                FileUtils::writeTxt("pkg.ver", QString::asprintf("%d", m_vars.minVer));
                // 开始下载游戏包
                m_qos.report(Qos::QosType::DOWNLOAD_PKG_START, "startdownload");
                qInfo() << "开始下载游戏包:" << pkgURL;
                m_state = GameState::DownloadPkg;

                appendDownload(DownloadTask::DT_Pkg, url, DOWNLOAD_GAMEPKG, Qos::QosType::DOWNLOAD_PKG_ERR, "downloaderr");
            }
        }
        else
        {
            qInfo() << "游戏已安装";
            onPkgReady();
        }
    }
    else if(GameState::DownloadPkg == m_state)
    {
        qInfo() << "游戏包下载结束";
        m_qos.report(Qos::QosType::DOWNLOAD_PKG_FINISH, "finishdownload");
        unzipGameZip();
    }
}

void GameLogic::onDownloadError(const QUrl &url)
{
    // 下载失败
    auto item = m_downloadTaskMap[url];
    m_qos.report(item.errQosType, item.errQosDesc.toStdString().c_str());
}

void GameLogic::onTimer()
{
    if(m_timerTaskFlag & TimerTask::CheckUnityLog)
    {
        checkUnityStatus();
    }
    if(m_timerTaskFlag & TimerTask::CheckUnityCmd)
    {
        checkUnityCmd();
    }
}

void GameLogic::checkUnityStatus()
{
    QString logFile = findUnityLog();
    if(logFile.isEmpty())
    {
        // 超过限定时间没有创建日志，判定为unity启动失败
        if(QDateTime::currentSecsSinceEpoch() - m_callUnityAt > UNITY_READY_TOLERANCE)
        {
            qWarning() << "未找到unity日志文件";
            uploadLog();
            return;
        }
    }

    auto logContent = FileUtils::readTxt(logFile, "");
    if(!logContent.isEmpty())
    {
        // 如果文件打不开，是因为unity独占了，那么啥也不做
        m_timerTaskFlag &= ~TimerTask::CheckUnityLog;
        if(logContent.contains("PlayerInitEngineGraphics: InitializeEngineGraphics failed"))
        {
            qInfo() << "unity运行异常";
            m_qos.report(Qos::QosType::LAUNCH_UNITY_DRIVERERR, "drivererr");
            uploadLog();
            QMessageBox msgbox(QMessageBox::Icon::Critical, UIUtils::winTitle(WIN_TITLE_XIANKA), MSG_UNITY_ERROR);
            msgbox.exec();
        }
        else
        {
            qInfo() << "unity运行正常";
        }
    }
}

void GameLogic::checkUnityCmd()
{
    auto pkgName = cfgMgr.readString<QString>(CONFIG_KEY_PKGNAME);
    QString cmdFile = PathUtils::resource(pkgName + "/" + pkgName + "_Data/fycmd/" + m_unityCmdKey + ".txt");
    if(!QFile::exists(cmdFile))
    {
        return;
    }

    auto cmdContent = FileUtils::readTxt(cmdFile, "");
    auto cmdLines = cmdContent.split(QRegularExpression("\r?\n"));
    if(cmdLines.size() >= 2)
    {
        auto cmdSeq = cmdLines[0].toInt();
        auto cmd = cmdLines[1];
        //auto cmdParas = cmdLines[2];
        if(cmdSeq > m_lastCmdSeq)
        {
            m_lastCmdSeq = cmdSeq;
            qInfo() << "收到unity消息:" << cmd;
            if (cmd == "quit")
            {
                safeExit();
            }
            else if (cmd == "reset")
            {
                if (GameMode::Simple != m_vars.mode)
                {
                    // 简易模式下，退出unity后微端不自动弹出
                    if(!m_window->isVisible())
                    {
                        QTimer::singleShot(DELAY_POP_WINDOW, this, &GameLogic::resetWindow);
                    }
                }
            }
            else if (cmd == "ok")
            {
                // unity ts运行成功
                m_isUnityOK = true;
            }
            else if (cmd == "canExit")
            {
                m_canExit = true;
            }
        }
    }
    if (QProcess::ProcessState::NotRunning == m_unityProc->state())
    {
        m_canExit = true;
        if(GameMode::Simple == m_vars.mode)
        {
            // 简易模式下，如果检查到unity进程退出了，微端也退出，比如玩家通过任务管理器杀进程
            qInfo() << "检测到unity进程已退出，简易模式下直接退出微端";
            safeExit();
        }
        else
        {
            // 普通模式下，则将微端重新弹出
            if(!m_window->isVisible())
            {
                qInfo() << "检测到unity进程已退出，准备弹出微端";
                QTimer::singleShot(DELAY_POP_WINDOW, this, &GameLogic::resetWindow);
            }
        }
    }
}

void GameLogic::uploadLog()
{
    qInfo() << "开始上传日志";
    auto now = QString::number(QDateTime::currentSecsSinceEpoch());
    auto uploadUrl = cfgMgr.readString<QString>(CONFIG_KEY_UPLOAD);
    // unity日志
    auto unityLogFile = findUnityLog();
    if(unityLogFile.isEmpty())
    {
        qWarning() << "未找到unity日志，不上传";
    }
    else
    {
        auto unityLogName = QString("%1_%2_%3_unity.txt").arg(m_vars.platform, now, m_vars.uid);
        m_uploader.upload(unityLogFile, uploadUrl, unityLogName, "logfile");
    }
    // 微端日志
    auto cliLogName = QString("%1_%2_%3_shell.txt").arg(m_vars.platform, now, m_vars.uid);
    m_uploader.upload(PathUtils::resource(RES_PREVLOGFILE), uploadUrl, cliLogName, "logfile");
}

void GameLogic::safeExit()
{
    if(m_unityProc)
    {
        disconnect(m_unityProc.get(), &QProcess::errorOccurred, this, 0);
        m_unityProc->kill();
        m_unityProc->close();
    }
#if defined (GM_PLAT_ZTJ_TOP1GAME)
    // 诛天记台服退出后要返回Box
    auto boxPath = cfgMgr.readString<QString>(CONFIG_KEY_BOXINSTALLEDPATH);
    QList<QString> params;
    params << cfgMgr.readString<QString>(CONFIG_KEY_GAMEID);
    m_app->exit();
    auto success = QProcess::startDetached(boxPath, params);
    if(success)
    {
        qInfo() << "退出微端拉起游戏盒子成功";
    }
    else
    {
        qWarning() << "退出微端拉起游戏盒子失败";
    }
#else
    QCoreApplication::exit();
#endif
}

void GameLogic::checkUpdate()
{
    if(m_hasCheckUpdate || GameMode::Hide == m_vars.mode || isUnityRunning())
    {
        qInfo() << "当前状态跳过更新";
        return;
    }
    auto pkgName = cfgMgr.readString<QString>(CONFIG_KEY_PKGNAME);
    if(!QFile::exists(PathUtils::resource(pkgName)))
    {
        qInfo() << "没有游戏包，跳过更新";
        return;
    }
    m_hasCheckUpdate = true;
    if(1 != cfgMgr.readString<int>(CONFIG_KEY_UPDATEFLAG))
    {
        qInfo() << "升级开关已关闭，跳过更新";
        return;
    }
    auto minShellVer = cfgMgr.readString<QString>(CONFIG_KEY_UPDATESHELLMIN);
    if(QCoreApplication::applicationVersion() < minShellVer)
    {
        // 需要升级，先静默下载微端更新包
        auto newShellPkgName = cfgMgr.readString<QString>(CONFIG_KEY_UPDATEPKGNAME);
        auto localNewShellPkg = PathUtils::resource(newShellPkgName);
        if(QFile::exists(localNewShellPkg))
        {
            qInfo() << "微端更新包已存在";
            continueUpdate();
        }
        else
        {
            auto latestURL = cfgMgr.readString<QString>(CONFIG_KEY_UPDATELATEST);
            latestURL = StringUtils::formatQString(latestURL, newShellPkgName);
            latestURL = StringUtils::formatQString(latestURL, QDateTime::currentSecsSinceEpoch());
            appendDownload(DownloadTask::DT_Shell, latestURL, DOWNLOAD_SHELLPKG, Qos::QosType::None, "");
        }
    }
    else
    {
        qInfo() << "无须更新微端";
    }
}

void GameLogic::continueUpdate()
{
    if(isUnityRunning()) return;

    // 先将当前程序更名
    auto newName = QCoreApplication::applicationFilePath().replace(".exe", "." + QCoreApplication::applicationVersion() + ".exe");
    if(QFile::exists(newName)) QFile::remove(newName);
    auto success = QFile::rename(QCoreApplication::applicationFilePath(), newName);
    if(!success)
    {
        qWarning() << "微端更名失败";
        return;
    }
    // 解压新微端
    qInfo() << "开始解压新微端";
    auto newShellPkgName = cfgMgr.readString<QString>(CONFIG_KEY_UPDATEPKGNAME);
    auto localNewShellPkg = PathUtils::resource(newShellPkgName);

    auto list = JlCompress::extractDir(localNewShellPkg, PathUtils::resource(""));
    QFile::remove(localNewShellPkg);
    qInfo() << "新微端解压结束，文件数量:" << list.size();
    if(list.size() == 0)
    {
        qWarning("新微端解压失败！");
        return;
    }
    qInfo() << "---------------------";
    qInfo() << list.join(", ");
    qInfo() << "---------------------";

    QMessageBox msgbox(QMessageBox::Icon::Question, UIUtils::winTitle(WIN_TITLE_UPDATE), MSG_UPDATE);
    QPushButton *btnOk = msgbox.addButton(BTN_LABEL_OK, QMessageBox::ButtonRole::AcceptRole);
    msgbox.addButton(BTN_LABEL_CANCEL, QMessageBox::ButtonRole::RejectRole);
    msgbox.exec();
    if(msgbox.clickedButton() == btnOk)
    {
        m_app->exit(EXITCODE_REBOOT);
    }
}

#ifdef GM_PLAT_ZTJ_TOP1GAME
void GameLogic::onZtjTop1GameLogin(QNetworkReply *reply)
{
    if(!reply->error())
    {
        auto data = reply->readAll();
        auto ret = JsonUtils::toJson(data);
        qInfo() << "登录回复:" << QString(data);
        if(ret.value("code").toInt() == 1)
        {
            auto gameUrl = ret.value("url").toString();
            m_window->gotoURL(gameUrl, cfgMgr.readString<QString>(CONFIG_KEY_JUMP));
            return;
        }
    }
    else
    {
        qInfo() << "登录请求error:" << reply->errorString();
    }

    QMessageBox msgbox(QMessageBox::Icon::Critical, UIUtils::winTitle(WIN_TITLE_ERROR), MSG_LOGIN_ERROR);
    msgbox.addButton(BTN_LABEL_OK, QMessageBox::ButtonRole::AcceptRole);
    msgbox.exec();
    safeExit();
}
#endif
